package codeflyer.server.classload;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import codeflyer.server.utils.AnalysisXml;
import codeflyer.server.utils.FileSearch;
import codeflyer.server.utils.JarUtils;


/**
 * @author CodeFlyer
 * @date May 4, 2020
 * @version 1.0
 */

//1解压webapp下的所有jar包,已解压的不会重复解压
//2遍历所有项目得到所有的类文件
//3通过解析每个项目的web.xml来得到需要加载的类

public class ClassLoad {
	
	private static List<String> jarList = new ArrayList<>();  //得到所有的jar包路径,已经解压则移除路径
	private static List<String> jarFolderList = new ArrayList<>(); //得到所有解压后的文件夹,带最后的斜杆
	private static Map<String, List<URL>> projectUrlsMap = new ConcurrentHashMap<String, List<URL>>(); //项目有斜杆和urls	
	private static Map<String, List<String>> projectClassMap = new HashMap<>();  //每个项目下的所有class文件,项目不带最后的斜杆
	private static Map<String, String> xmlMap = new HashMap<>();  //存储了项目名称和对应的web.xml
	private static String resourcePath = new File("").getAbsolutePath().replaceAll("\\\\", "/") + "/webapp";
//	private static URLClassLoader urlClassLoader = null; 
	private static Map<String, URLClassLoader> proClassLoader = new ConcurrentHashMap<String, URLClassLoader>(); //项目无斜杆和类加载器
	
	
	public static List<String> getJarList() {
		return jarList;
	}

	public static List<String> getJarFolderList() {
		return jarFolderList;
	}


	public static Map<String, List<String>> getProjectClassMap() {
		return projectClassMap;
	}

	public static Map<String, String> getXmlMap() {
		return xmlMap;
	}

	public static Map<String, List<URL>> getProjectUrlsMap() {
		return projectUrlsMap;
	}

	public static String getResourcePath() {
		return resourcePath;
	}

	public static Map<String, URLClassLoader> getProClassLoader() {
		return proClassLoader;
	}

	//jar包所在路径是通过动态查找得到的,要输出的路径就是该jar包的名称,得到的jar包必须要在尾部携带cf
	public static boolean myClassLoad() {
		System.out.println("webapp的路径为: "+resourcePath);
		//通过serachFile找到jar包,如果已经存在解压的文件夹在不进行解压
		FileSearch fileSearch = new FileSearch();
		fileSearch.firstList(resourcePath);     //只查找webapp下所有文件和目录
		List<String> fileList = fileSearch.getWebappList();
		for (String string : fileList) {
			if(string.lastIndexOf(".jar")!=-1) {  //如果是jar而且文件名最后以cf结尾
				if(string.substring(0, string.lastIndexOf(".jar")).lastIndexOf("_cf")!=-1) {
					jarList.add(string);
				}
			}		
		}
		System.out.println("jarList的所有jar包"+jarList);
		
		//使用JarUtils解压jar包
		 //检测jar包有没有被解压,防止重复解压,并把已经解压的jar包的文件夹放入list中
		//这里用while循环是因为,遍历集合然后对其进行remove操作回报错,while循环可以解决该问题
		Iterator<String> iterator = jarList.iterator();
		while (iterator.hasNext()) {  //把所有的jar包进行解压,解压之前查看是否已经解压过
			String string = iterator.next();
			FileSearch checkJar = new FileSearch(); //文件夹和文件
			checkJar.iterateFile(resourcePath);  //搜索webapp下的所有文件
			List<String> jars = checkJar.getFolderList();
			for (String jarFolder : jars) {  //会遍历四次
				String jarString = string.substring(0, string.length()-4); //项目绝对路径
				if(jarString.equals(jarFolder)) {
					//jarList.remove(string);   //如果已经解压则把该jar路径移除
					iterator.remove();  //移除当前遍历的这个元素
				}
				if(jarFolder.lastIndexOf("cf")!=-1) {
					//所有以cf结尾的文件夹都应该存放到已解压jar包文件夹list中
					if(!jarFolderList.contains(jarString+"/")) {
						jarFolderList.add(jarString+"/");
					}					
				}
			}
		}
		
		//开始解压jar包到指定目录,得到所有jar包的绝对路径,得到jar包解压后的同名文件夹
		if(jarList.size()>0) {			 
			for (String jarPath : jarList) {
				//得到解压路径
//				String jarFilePath = jarPath.split("\\.")[0];  //分割之后的第1个字符串
				String jarFilePath = jarPath.substring(0, jarPath.lastIndexOf(".jar"));
				//进行解压				
				JarUtils.unJar(new File(jarPath), new File(jarFilePath));
				jarFolderList.add(jarFilePath+"/");  //这里末尾需要添加斜杆"/"			
			}
		}
			 			
		//把解压得到的文件夹和已近解压得到的文件夹路径放入到URLClassLoader中
		//这一步应该在搜索jar包之前判断一下,是否包含cf结尾的文件夹路径,有的话直接加载不需要重复步骤
		if(jarFolderList.size()>0) {
			//把List转为数组
			for (String string : jarFolderList) {
				try {
//					jarFolderUrls.add(new File(string).toURI().toURL());
					URL proClassUrl = new File(string).toURI().toURL();
					if(projectUrlsMap.get(string)!=null) {
						projectUrlsMap.get(string).add(proClassUrl);
					}else {
						List<URL> uList = new ArrayList<>();
						uList.add(proClassUrl);
						projectUrlsMap.put(string, uList);
					}
					//得到项目下所有lib目录的jar包url
					FileSearch fs = new FileSearch();
					fs.iterateFile(string+"lib");
					List<String> lib = fs.getFileList();
					for (String libjar : lib) {
						if(libjar.endsWith(".jar")) {
							URL libJarURL = new File(libjar).toURI().toURL();
							projectUrlsMap.get(string).add(libJarURL);
						}					
					}
				} catch (MalformedURLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}  //把文件路径转为url
			}
			//把所有的项目分开进行加载,使用不同的类加载器
			for (Entry<String, List<URL>> urlEntry : projectUrlsMap.entrySet()) {
				if(proClassLoader.get(urlEntry.getKey())==null) {
					List<URL> urlList = urlEntry.getValue();
					int length = urlList.size();
					URL[] urls = new URL[length];
					urlList.toArray(urls);
					//加载某个项目下的所有class文件以及项目lib目录下的所有jar包
					URLClassLoader urlClassLoader = new URLClassLoader(urls,Thread.currentThread().getContextClassLoader());
					String projectP = urlEntry.getKey();
					projectP = projectP.substring(0, projectP.lastIndexOf("/"));  //去除最后一个斜杆
					proClassLoader.put(projectP, urlClassLoader);
				}
			}
		}else {
			System.out.println("没有项目文件夹");
			return false;   //表示没有jar包也没有任何包含后缀为cf的文件
		}
				
		//使用SearchFIle得到所有class文件路径,这个路径只需要是解压后文件夹的相对路径即可
		for (String jarFile : jarFolderList) {
			System.out.println("项目文件夹个数------>"+jarFolderList.size());
			FileSearch file = new FileSearch();
			jarFile = jarFile.substring(0, jarFile.lastIndexOf("/"));  //去除最后一个斜杆
			String projectName = jarFile.substring(jarFile.lastIndexOf("/"));  //得到项目名称
			file.iterateFile(jarFile);
			List<String> jfList = file.getFileList();  //得到所有文件绝对路径
			for (String classFile : jfList) {
				if(classFile.endsWith(".class")) {
					String cN = "";
					//需要得到相对路径,不包括项目名称,而且把所有"/"换成"."去除class
					cN = className(classFile,projectName);  //得到全限定类名
					if(!projectClassMap.containsKey(jarFile)) {  //该项目下还没有创建list的时
						List<String> classList = new ArrayList<>();						
						projectClassMap.put(jarFile, classList);
						projectClassMap.get(jarFile).add(cN);
					}else {
						projectClassMap.get(jarFile).add(cN);
					}
				}else if(classFile.endsWith("web.xml")) {  //这里是绝对路径,所以是结尾为web.xml
					if(!xmlMap.containsKey(jarFile)) {
						xmlMap.put(jarFile, classFile);
					}
				}
			}
		}
				
		
		//调用classload方法来加载指定类,要加载的类也就是在web.xml中指定了的类
		  //web.xml要指定全限定类名,拦截路径,跳转路径,映射路径
		//得到web.xml的绝对路径
		for (String jf : jarFolderList) {
			jf = jf.substring(0, jf.lastIndexOf("/"));  //去除最后一个斜杆
			String xmlPath = xmlMap.get(jf);
			System.out.println("xml的路径:"+xmlPath);  //没有得到路径
			//调用解析xml的类,并区分每个项目的xml内容
			AnalysisXml.analysisXml(xmlPath,jf);    //保存了路径和servlet类名的键值对
			//把解析后的键值对放入到一个项目绝对路径为键的map中
		}
				
		//以上成功之后
		return true;
	}
	
	//把class文件路径变为类的全限定类名
	private static String className(String classPath,String projectName) {
		//需要得到相对路径,不包括项目名称,而且把所有"/"换成"."去除class
		//去除包括项目名称的字符串
		String[] strings =  classPath.split(projectName);
		String string = strings[1];
		string = string.substring(1);  //去除第一个斜杆
		string = string.replaceAll("/", "\\."); //所有斜杆换成点
		string = string.substring(0,string.lastIndexOf(".class")); //左闭右开
		System.out.println("我是文件夹下的所有类"+string);
		return string;
	}
	
	
}


